﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Forms.Integration;
using Silverlight.Hosting.Wpf.Private;
using Silverlight.Hosting.Wpf.Interop;
using System.Windows.Interop;
using System.Runtime.InteropServices;
using System.IO;
using EventArgs = System.Windows.RoutedEventArgs;

namespace Silverlight.Hosting.Wpf
{
    public class SilverlightWindowlessHost : FrameworkElement, ISilverlightHost, IDisposable
    {
        private bool _disposed;
        private bool _renderingEnabledCore;
        private Window _window;
        private HwndSource _source;
        private XcpWindowlessContainerProxy _container;
        private RasterRendererElement _renderer;
        private static Vector? _dpiVector;
        private readonly static Type _ownerType = typeof(SilverlightWindowlessHost);

        static SilverlightWindowlessHost()
        {
            FocusableProperty.OverrideMetadata(typeof(SilverlightWindowlessHost), new FrameworkPropertyMetadata(true));
        }

        public SilverlightWindowlessHost()
        {
            this.Loaded += OnIsLoadedChanged;
            this.Unloaded += OnIsLoadedChanged;

            _container = new XcpWindowlessContainerProxy(this);
            _renderer = new InteropBitmapRenderer();

            AddVisualChild(_renderer);

            IsRenderingEnabledCore = IsRenderingEnabled;
        }

        #region Dependency Properties

        private static readonly DependencyProperty IsRenderingEnabledProperty = DependencyProperty.Register(
            "IsRenderingEnabled", typeof(bool), _ownerType, new PropertyMetadata(true, OnIsRenderingEnabledChanged));

        private static readonly DependencyPropertyKey IsWindowlessPropertyKey = DependencyProperty.RegisterReadOnly(
            "IsWindowless", typeof(bool), _ownerType, new PropertyMetadata(true));
        public static readonly DependencyProperty IsWindowlessProperty = IsWindowlessPropertyKey.DependencyProperty;

        private static readonly DependencyPropertyKey IsCreatedPropertyKey = DependencyProperty.RegisterReadOnly(
            "IsCreated", typeof(bool), _ownerType, new PropertyMetadata(false));
        public static readonly DependencyProperty IsCreatedProperty = IsCreatedPropertyKey.DependencyProperty;

        public static readonly DependencyProperty BaseUrlProperty = DependencyProperty.Register(
            "BaseUrl", typeof(Uri), _ownerType, new PropertyMetadata(null, OnBaseUrlChanged));

        public static readonly DependencyProperty HostOptionsProperty = DependencyProperty.Register(
            "HostOptions", typeof(XcpHostOptions), _ownerType, new PropertyMetadata(XcpHostOptions.Default, OnHostOptionsChanged));

        public static readonly DependencyProperty VersionProperty = DependencyProperty.Register(
            "Version", typeof(Version), _ownerType, new PropertyMetadata(new Version(4, 0), OnVersionChanged));

        public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
            "Source", typeof(Uri), _ownerType, new PropertyMetadata(null, OnSourceChanged));

        public static readonly DependencyProperty XamlProperty = DependencyProperty.Register(
            "Xaml", typeof(string), _ownerType, new PropertyMetadata(null, OnXamlChanged));

        private static readonly DependencyPropertyKey IsSilverlightLoadedPropertyKey = DependencyProperty.RegisterReadOnly(
            "IsSilverlightLoaded", typeof(bool), _ownerType, new PropertyMetadata(false));
        public static readonly DependencyProperty IsSilverlightLoadedProperty = IsSilverlightLoadedPropertyKey.DependencyProperty;

        private static readonly DependencyPropertyKey ContentPropertyKey = DependencyProperty.RegisterReadOnly(
            "Content", typeof(Content), _ownerType, new PropertyMetadata());
        public static readonly DependencyProperty ContentProperty = ContentPropertyKey.DependencyProperty;

        private static readonly DependencyPropertyKey SettingsPropertyKey = DependencyProperty.RegisterReadOnly(
            "Settings", typeof(Settings), _ownerType, new PropertyMetadata());
        public static readonly DependencyProperty SettingsProperty = SettingsPropertyKey.DependencyProperty;

        public static readonly DependencyProperty InitParamsProperty = DependencyProperty.Register(
            "InitParams", typeof(string), _ownerType, new PropertyMetadata(null, OnInitParamsChanged));

        public static readonly DependencyProperty SplashScreenSourceProperty = DependencyProperty.Register(
            "SplashScreenSource", typeof(Uri), _ownerType, new PropertyMetadata(null, OnSplashScreenSourceChanged));

        public static readonly RoutedEvent CreatedEvent = EventManager.RegisterRoutedEvent(
            "Created", RoutingStrategy.Bubble, typeof(RoutedEventHandler), _ownerType);

        public static readonly RoutedEvent SilverlightLoadedEvent = EventManager.RegisterRoutedEvent(
            "SilverlightLoaded", RoutingStrategy.Bubble, typeof(RoutedEventHandler), _ownerType);

        public static readonly RoutedEvent SilverlightErrorEvent = EventManager.RegisterRoutedEvent(
            "SilverlightError", RoutingStrategy.Bubble, typeof(RoutedEventHandler<SilverlightErrorEventArgs>), _ownerType);

        public static readonly RoutedEvent SilverlightEventEvent = EventManager.RegisterRoutedEvent(
            "SilverlightEvent", RoutingStrategy.Bubble, typeof(RoutedEventHandler<SilverlightEventArgs>), _ownerType);

        public static readonly RoutedEvent SourceDownloadCompleteEvent = EventManager.RegisterRoutedEvent(
            "SourceDownloadComplete", RoutingStrategy.Bubble, typeof(RoutedEventHandler), _ownerType);

        public static readonly RoutedEvent SourceDownloadProgressChangedEvent = EventManager.RegisterRoutedEvent(
            "SourceDownloadProgressChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler<SourceDownloadProgressChangedEventArgs>), _ownerType);

        #endregion

        #region Public Events

        /// <summary>
        /// Raised when the Silverlight control is created.
        /// </summary>
        public event RoutedEventHandler Created
        {
            add { AddHandler(CreatedEvent, value); }
            remove { RemoveHandler(CreatedEvent, value); }
        }

        /// <summary>
        /// Raised whenever the XAML DOM has successfully loaded (i.e. the application
        /// is fully loaded and ready to display). This notification is useful because
        /// loading Silverlight can take longer for complex applications and when loading
        /// the CLR for the first time. 
        /// </summary>
        public event RoutedEventHandler SilverlightLoaded
        {
            add { AddHandler(SilverlightLoadedEvent, value); }
            remove { RemoveHandler(SilverlightLoadedEvent, value); }
        }

        /// <summary>
        /// Raised whenever errors are raised from within the XAML DOM of the hosted control.
        /// This event can indicate when a load failure has occurred, for example, when the
        /// expected .xap file does not exist. You can also use this event to respond to XAML
        /// parsing errors and runtime errors while using the JavaScript API for Silverlight.
        /// </summary>
        public event RoutedEventHandler<SilverlightErrorEventArgs> SilverlightError
        {
            add { AddHandler(SilverlightErrorEvent, value); }
            remove { RemoveHandler(SilverlightErrorEvent, value); }
        }

        /// <summary>
        /// Raised whenever events occur within the object model of the hosted control.
        /// </summary>
        public event RoutedEventHandler<SilverlightEventArgs> SilverlightEvent
        {
            add { AddHandler(SilverlightEventEvent, value); }
            remove { RemoveHandler(SilverlightEventEvent, value); }
        }

        /// <summary>
        /// Raised when the source download has finished.
        /// </summary>
        public event RoutedEventHandler SourceDownloadComplete
        {
            add { AddHandler(SourceDownloadCompleteEvent, value); }
            remove { RemoveHandler(SourceDownloadCompleteEvent, value); }
        }

        /// <summary>
        /// Raised when the source download progress changes.
        /// </summary>
        public event RoutedEventHandler<SourceDownloadProgressChangedEventArgs> SourceDownloadProgressChanged
        {
            add { AddHandler(SourceDownloadProgressChangedEvent, value); }
            remove { RemoveHandler(SourceDownloadProgressChangedEvent, value); }
        }

        #endregion

        #region Protected Properties

        /// <summary>
        /// Gets or sets a value indicating if the Silverlight plug-in should be rendered.
        /// </summary>
        protected virtual bool IsRenderingEnabledCore
        {
            get { return _renderingEnabledCore; }
            set
            {
                if (value && !_renderingEnabledCore)
                {
                    // The on rendered event informs us when a new frame has been rendered
                    // and is ready for presentation
                    _container.FrameRendered += OnSilverlightFrameRendered;
                }
                else if (!value)
                {
                    _container.FrameRendered -= OnSilverlightFrameRendered;
                }

                _renderingEnabledCore = value;
            }
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets a value indicating if the Silverlight plug-in should be rendered.
        /// </summary>
        public bool IsRenderingEnabled
        {
            get { return (bool)GetValue(IsRenderingEnabledProperty); }
            set { SetValue(IsRenderingEnabledProperty, value); }
        }

        /// <summary>
        /// Gets a value indicating whether the control has been disposed of.
        /// </summary>
        public bool IsDisposed
        {
            get { return _disposed; }
        }

        /// <summary>
        /// Gets a value indicating whether the control is windowless.
        /// </summary>
        public bool IsWindowless
        {
            get { return (bool)GetValue(IsWindowlessProperty); }
        }

        /// <summary>
        /// Gets a value indicating whether the control has been created.
        /// </summary>
        public bool IsCreated
        {
            get { return GetProperty(IsCreatedPropertyKey, _container.IsCreated); }
        }

        /// <summary>
        /// Gets or sets the URL that the hosted control should use as the base for any relative requests made for Source,
        /// to the downloader, etc.
        /// </summary>
        public Uri BaseUrl
        {
            get { return GetProperty(BaseUrlProperty, _container.BaseUrl); }
            set { SetValue(BaseUrlProperty, value); }
        }

        /// <summary>
        /// Gets or sets the XcpHostOptions specified by the host.
        /// </summary>
        public XcpHostOptions HostOptions
        {
            get { return GetProperty(HostOptionsProperty, _container.HostOptions); }
            set { SetValue(HostOptionsProperty, value); }
        }

        /// <summary>
        /// Gets or sets the version of the intalled Silverlight plug-in.
        /// </summary>
        [System.ComponentModel.TypeConverter(typeof(VersionConverter))]
        public Version Version
        {
            get { return GetProperty(VersionProperty, _container.Version); }
            set { SetValue(VersionProperty, value); }
        }


        /// <summary>
        /// Gets or sets the URI specified for the source content (can be a XAML page or a package, depending on
        /// which programming model is used for the Silverlight content).
        /// </summary>
        public Uri Source
        {
            get { return GetProperty(SourceProperty, _container.Source); }
            set { SetValue(SourceProperty, value); }
        }

        /// <summary>
        /// Gets or sets the XAML content dispayed in the Silverlight plug-in.
        /// </summary>
        public string Xaml
        {
            get { return GetProperty(XamlProperty, _container.Xaml); }
            set { SetValue(XamlProperty, value); }
        }

        /// <summary>
        /// Gets a Boolean that declares whether the control's onLoad event has been raised.
        /// </summary>
        public bool IsSilverlightLoaded
        {
            get { return GetProperty(IsSilverlightLoadedPropertyKey, _container.IsLoaded); }
        }

        /// <summary>
        /// Gets a Boolean that declares whether the control's onLoad event has been raised.
        /// </summary>
        bool ISilverlightHost.IsLoaded
        {
            get { return IsSilverlightLoaded; }
        }

        /// <summary>
        /// Gets the content sub-object.
        /// </summary>
        public Content Content
        {
            get { return GetProperty(ContentPropertyKey, _container.Content); }
        }

        /// <summary>
        /// Gets the settings sub-object.
        /// </summary>
        public Settings Settings
        {
            get { return GetProperty(SettingsPropertyKey, _container.Settings); }
        }

        /// <summary>
        /// Gets or sets initialization parameters.
        /// </summary>
        public string InitParams
        {
            get { return GetProperty(InitParamsProperty, _container.InitParams); }
            set { SetValue(InitParamsProperty, value); }
        }

        /// <summary>
        /// Gets or sets the URI specified for the splash screen source XAML.
        /// </summary>
        public Uri SplashScreenSource
        {
            get { return GetProperty(SplashScreenSourceProperty, _container.SplashScreenSource); }
            set { SetValue(SplashScreenSourceProperty, value); }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Loads the CLR runtime
        /// </summary>
        public void LoadRuntime()
        {
            _container.LoadRuntime();
        }

        /// <summary>
        /// Creates a new instance of a Silverlight helper object.
        /// </summary>
        /// <param name="objectType">Specifies the type of object to create.</param>
        /// <returns>The resulting object.</returns>
        public SafeComObject<IXcpObject> CreateObject(string objectType)
        {
            return _container.CreateObject(objectType);
        }

        /// <summary>
        /// Determines whether the existing Silverlight plug-in
        /// is a matching version or is a compatible version with a specified version string.
        /// </summary>
        /// <param name="version">String specifying the version to check.</param>
        /// <returns>True if the version is supported; otherwise, False.</returns>
        public bool IsVersionSupported(string version)
        {
            return _container.IsVersionSupported(version);
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// Called by the hosted control whenever a download is requested. Hosts should implement this method to provide
        /// specialized download behavior for cases where the download might request cross-domain URLs, or where the host
        /// has other restrictions on requests made as a URI.
        /// </summary>
        /// <param name="url">The URL to download.</param>
        /// <param name="callback">The downloader callback implementation.</param>
        /// <param name="stream">The stream containing the downloaded content.</param>
        /// <returns>Returns OK if the download was handled, Pending if it is being done asynchronously, or Default
        /// if Silverlight should handle the download.</returns>
        protected virtual DownloadResult DownloadUrl(string url, IDownloadUrlCallback callback, out Stream stream)
        {
            return _container.InvokeBaseDownloadUrl(url, callback, out stream);
        }

        /// <summary>
        /// Called when the Silverlight control is created.
        /// </summary>
        protected virtual void OnCreated(EventArgs e)
        {
            _container.InvokeBaseOnCreated(e);

            e.RoutedEvent = CreatedEvent;
            e.Source = this;
            RaiseEvent(e);
        }

        /// <summary>
        /// Called whenever the XAML DOM has successfully loaded.
        /// </summary>
        protected virtual void OnSilverlightLoaded(EventArgs e)
        {
            _container.InvokeBaseOnSilverlightLoaded(e);

            e.RoutedEvent = SilverlightLoadedEvent;
            e.Source = this;
            RaiseEvent(e);
        }

        /// <summary>
        /// Called whenever errors are raised from within the XAML DOM of the hosted control.
        /// </summary>
        /// <param name="e">The details of the error.</param>
        protected virtual void OnSilverlightError(SilverlightErrorEventArgs e)
        {
            _container.InvokeBaseOnSilverlightError(e);

            e.RoutedEvent = SilverlightErrorEvent;
            e.Source = this;
            RaiseEvent(e);
        }

        /// <summary>
        /// Invoked whenever events occur within the object model of the hosted control.
        /// </summary>
        /// <param name="e">The details of the event.</param>
        protected virtual void OnSilverlightEvent(SilverlightEventArgs e)
        {
            _container.InvokeBaseOnSilverlightEvent(e);

            e.RoutedEvent = SilverlightEventEvent;
            e.Source = this;
            RaiseEvent(e);
        }

        /// <summary>
        /// Called when the source download has finished.
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnSourceDownloadComplete(EventArgs e)
        {
            _container.InvokeBaseOnSourceDownloadComplete(e);

            e.RoutedEvent = SourceDownloadCompleteEvent;
            e.Source = this;
            RaiseEvent(e);
        }

        /// <summary>
        /// Called when the source download progress changes.
        /// </summary>
        /// <param name="e">The details of the download progress.</param>
        protected virtual void OnSourceDownloadProgressChanged(SourceDownloadProgressChangedEventArgs e)
        {
            _container.InvokeBaseOnSourceDownloadProgressChanged(e);

            e.RoutedEvent = SourceDownloadProgressChangedEvent;
            e.Source = this;
            RaiseEvent(e);
        }

        /// <summary>
        /// Called after the content in the Silverlight plug-in has completely loaded.
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnContentLoaded(EventArgs e)
        {
            _container.InvokeBaseOnContentLoaded(e);
        }

        /// <summary>
        /// Called when when the Silverlight plug-in generates a XAML parse error or
        /// run-time error at the native-code level.
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnContentError(ErrorEventArgs e)
        {
            _container.InvokeBaseOnContentError(e);
        }

        /// <summary>
        /// Called when the Silverlight plug-in content area receives a host-generated zoom event.
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnContentZoomed(EventArgs e)
        {
            _container.InvokeBaseOnContentZoomed(e);
        }

        /// <summary>
        /// Called when the ActualHeight or ActualWidth of the Silverlight plug-in change.
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnContentResized(EventArgs e)
        {
            _container.InvokeBaseOnContentResized(e);
        }

        /// <summary>
        /// Called whenever the FullScreen property of the Silverlight plug-in changes.
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnFullScreenChanged(EventArgs e)
        {
            _container.InvokeBaseOnFullScreenChanged(e);
        }

        protected override void OnInitialized(System.EventArgs e)
        {
            base.OnInitialized(e);

            _container.CreateControl();
        }

        protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
        {
            base.OnRenderSizeChanged(sizeInfo);
            SetContainerSize(sizeInfo.NewSize);
        }

        protected void SetContainerSize(Size sz)
        {
            Vector dpi = GetDpiVector();
            System.Drawing.Size size = new System.Drawing.Size(
                (int)(sz.Width * dpi.X),
                (int)(sz.Height * dpi.Y));

            _container.Size = size;
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            _renderer.Measure(availableSize);
            return _renderer.DesiredSize;
            //return new Size(300,300);
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            _renderer.Arrange(new Rect(finalSize));
            return finalSize;
        }

        protected override int VisualChildrenCount
        {
            get
            {
                return 1;
            }
        }

        protected override Visual GetVisualChild(int index)
        {
            if (index > 0)
                throw new IndexOutOfRangeException();

            return _renderer;
        }

        protected virtual void OnParentWindowClosed(object sender, System.EventArgs e)
        {
            _source.RemoveHook(WndProc);
            _source = null;
            _window.Closed -= OnParentWindowClosed;
            _window = null;
        }

        protected virtual void OnIsLoadedChanged(object sender, RoutedEventArgs e)
        {
            IsRenderingEnabledCore = (IsLoaded && IsRenderingEnabled);

            if (IsLoaded)
            {
                _source = (HwndSource)HwndSource.FromVisual(this);
                _source.AddHook(WndProc);
                _window = Window.GetWindow(this);
                _window.Closed += OnParentWindowClosed;
            }
            else
            {
                OnParentWindowClosed(sender, e);
            }
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);

            if (IsRenderingEnabledCore)
            {
                var msg = ComponentDispatcher.CurrentKeyboardMessage;

                if (!ProcessInput(msg))
                {
                    DispatchMessage(msg);
                }
                TranslateMessage(ref msg);
                e.Handled = (e.Key != Key.System);
            }
        }

        protected override void OnKeyUp(KeyEventArgs e)
        {
            base.OnKeyUp(e);

            if (IsRenderingEnabledCore)
            {
                var msg = ComponentDispatcher.CurrentKeyboardMessage;

                if (!ProcessInput(msg))
                {
                    DispatchMessage(msg);
                }
                TranslateMessage(ref msg);
                e.Handled = (e.Key != Key.System);
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (IsRenderingEnabledCore)
            {
                ModifierFlags flags = 0;
                if (e.LeftButton == MouseButtonState.Pressed) flags |= ModifierFlags.MK_LBUTTON;
                if (e.RightButton == MouseButtonState.Pressed) flags |= ModifierFlags.MK_RBUTTON;
                if (e.MiddleButton == MouseButtonState.Pressed) flags |= ModifierFlags.MK_MBUTTON;
                if (e.XButton1 == MouseButtonState.Pressed) flags |= ModifierFlags.MK_XBUTTON1;
                if (e.XButton2 == MouseButtonState.Pressed) flags |= ModifierFlags.MK_XBUTTON2;
                if (Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) flags |= ModifierFlags.MK_CONTROL;
                if (Keyboard.Modifiers.HasFlag(ModifierKeys.Shift)) flags |= ModifierFlags.MK_SHIFT;

                Vector dpi = GetDpiVector();
                Point pt = e.GetPosition(this);

                var msg = new System.Windows.Interop.MSG()
                {
                    hwnd = _source.Handle,
                    message = (int)WindowMessages.WM_MOUSEMOVE,
                    wParam = (IntPtr)flags,
                    lParam = MakeLParam((int)(pt.X * dpi.X), (int)(pt.Y * dpi.Y))
                };

                e.Handled = true;
                if (!ProcessInput(msg))
                {
                    e.Handled = DispatchMessage(msg);
                }
            }
        }

        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            base.OnMouseDown(e);

            if (IsRenderingEnabledCore)
            {
                ModifierFlags flags = 0;
                if (e.LeftButton == MouseButtonState.Pressed) flags |= ModifierFlags.MK_LBUTTON;
                if (e.RightButton == MouseButtonState.Pressed) flags |= ModifierFlags.MK_RBUTTON;
                if (e.MiddleButton == MouseButtonState.Pressed) flags |= ModifierFlags.MK_MBUTTON;
                if (e.XButton1 == MouseButtonState.Pressed) flags |= ModifierFlags.MK_XBUTTON1;
                if (e.XButton2 == MouseButtonState.Pressed) flags |= ModifierFlags.MK_XBUTTON2;
                if (Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) flags |= ModifierFlags.MK_CONTROL;
                if (Keyboard.Modifiers.HasFlag(ModifierKeys.Shift)) flags |= ModifierFlags.MK_SHIFT;

                Vector dpi = GetDpiVector();
                Point pt = e.GetPosition(this);

                var msg = new System.Windows.Interop.MSG()
                {
                    hwnd = _source.Handle,
                    wParam = (IntPtr)flags,
                    lParam = MakeLParam((int)(pt.X * dpi.X), (int)(pt.Y * dpi.Y))
                };

                switch (e.ChangedButton)
                {
                    default:
                    case MouseButton.Left:
                        msg.message = (int)WindowMessages.WM_LBUTTONDOWN;
                        //CaptureMouse();
                        break;
                    case MouseButton.Middle:
                        msg.message = (int)WindowMessages.WM_MBUTTONDOWN;
                        break;
                    case MouseButton.Right:
                        msg.message = (int)WindowMessages.WM_RBUTTONDOWN;
                        break;
                    case MouseButton.XButton1:
                        msg.message = (int)WindowMessages.WM_XBUTTONDOWN;
                        msg.wParam = (IntPtr)((0x0001 << 16) | ((int)flags & 0xffff));
                        break;
                    case MouseButton.XButton2:
                        msg.message = (int)WindowMessages.WM_XBUTTONDOWN;
                        msg.wParam = (IntPtr)((0x0002 << 16) | ((int)flags & 0xffff));
                        break;
                }

                e.Handled = true;
                if (!ProcessInput(msg))
                {
                    e.Handled = DispatchMessage(msg);
                }
            }
        }

        protected override void OnMouseUp(MouseButtonEventArgs e)
        {
            base.OnMouseUp(e);

            if (IsRenderingEnabledCore)
            {
                //ReleaseMouseCapture();

                ModifierFlags flags = 0;
                if (e.LeftButton == MouseButtonState.Pressed) flags |= ModifierFlags.MK_LBUTTON;
                if (e.RightButton == MouseButtonState.Pressed) flags |= ModifierFlags.MK_RBUTTON;
                if (e.MiddleButton == MouseButtonState.Pressed) flags |= ModifierFlags.MK_MBUTTON;
                if (e.XButton1 == MouseButtonState.Pressed) flags |= ModifierFlags.MK_XBUTTON1;
                if (e.XButton2 == MouseButtonState.Pressed) flags |= ModifierFlags.MK_XBUTTON2;
                if (Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) flags |= ModifierFlags.MK_CONTROL;
                if (Keyboard.Modifiers.HasFlag(ModifierKeys.Shift)) flags |= ModifierFlags.MK_SHIFT;

                Vector dpi = GetDpiVector();
                Point pt = e.GetPosition(this);

                var msg = new System.Windows.Interop.MSG()
                {
                    hwnd = _source.Handle,
                    wParam = (IntPtr)flags,
                    lParam = MakeLParam((int)(pt.X * dpi.X), (int)(pt.Y * dpi.Y))
                };

                switch (e.ChangedButton)
                {
                    default:
                    case MouseButton.Left:
                        msg.message = (int)WindowMessages.WM_LBUTTONUP;
                        break;
                    case MouseButton.Middle:
                        msg.message = (int)WindowMessages.WM_MBUTTONUP;
                        break;
                    case MouseButton.Right:
                        msg.message = (int)WindowMessages.WM_RBUTTONUP;
                        break;
                    case MouseButton.XButton1:
                        msg.message = (int)WindowMessages.WM_XBUTTONUP;
                        msg.wParam = (IntPtr)((0x0001 << 16) | ((int)flags & 0xffff));
                        break;
                    case MouseButton.XButton2:
                        msg.message = (int)WindowMessages.WM_XBUTTONUP;
                        msg.wParam = (IntPtr)((0x0002 << 16) | ((int)flags & 0xffff));
                        break;
                }

                e.Handled = true;
                if (!ProcessInput(msg))
                {
                    e.Handled = true;
                    if (!DispatchMessage(msg))
                    {
                        // Send a context menu message
                        msg.message = (int)WindowMessages.WM_CONTEXTMENU;
                        msg.wParam = _source.Handle;
                        e.Handled = true;
                        if (!ProcessInput(msg))
                        {
                            e.Handled = DispatchMessage(msg);
                        }
                    }
                }
            }
        }

        protected override void OnMouseWheel(MouseWheelEventArgs e)
        {
            base.OnMouseWheel(e);

            if (IsRenderingEnabledCore)
            {
                ModifierFlags flags = 0;
                if (e.LeftButton == MouseButtonState.Pressed) flags |= ModifierFlags.MK_LBUTTON;
                if (e.RightButton == MouseButtonState.Pressed) flags |= ModifierFlags.MK_RBUTTON;
                if (e.MiddleButton == MouseButtonState.Pressed) flags |= ModifierFlags.MK_MBUTTON;
                if (e.XButton1 == MouseButtonState.Pressed) flags |= ModifierFlags.MK_XBUTTON1;
                if (e.XButton2 == MouseButtonState.Pressed) flags |= ModifierFlags.MK_XBUTTON2;
                if (Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) flags |= ModifierFlags.MK_CONTROL;
                if (Keyboard.Modifiers.HasFlag(ModifierKeys.Shift)) flags |= ModifierFlags.MK_SHIFT;

                Vector dpi = GetDpiVector();
                Point pt = e.GetPosition(this);

                var msg = new System.Windows.Interop.MSG()
                {
                    hwnd = _source.Handle,
                    message = (int)WindowMessages.WM_MOUSEWHEEL,
                    wParam = (IntPtr)(((short)e.Delta << 16) | (int)flags),
                    lParam = MakeLParam((int)(pt.X * dpi.X), (int)(pt.Y * dpi.Y))
                };

                e.Handled = true;
                if (!ProcessInput(msg))
                {
                    e.Handled = DispatchMessage(msg);
                }
            }
        }

        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);

            if (IsRenderingEnabledCore)
            {
                _container.UIActivate();
            }
        }

        protected override void OnLostFocus(EventArgs e)
        {
            base.OnLostFocus(e);

            if (IsRenderingEnabledCore)
            {
                _container.UIDeactivate();

                DispatchMessage(new System.Windows.Interop.MSG()
                {
                    hwnd = _source.Handle,
                    message = (int)WindowMessages.WM_KILLFOCUS,
                    wParam = _source.Handle
                });

                DispatchMessage(new System.Windows.Interop.MSG()
                {
                    hwnd = _source.Handle,
                    message = (int)WindowMessages.WM_IME_SETCONTEXT,
                    wParam = IntPtr.Zero,
                    lParam = (IntPtr)unchecked((int)0xC000000F) /*ISC_SHOWUIALL*/
                });
            }
        }

        protected bool DispatchMessage(System.Windows.Interop.MSG msg)
        {
            var m = System.Windows.Forms.Message.Create(msg.hwnd, msg.message, msg.wParam, msg.lParam);
            return _container.DispatchMessage(ref m);
        }

        protected bool ProcessInput(System.Windows.Interop.MSG msg)
        {
            bool processed = true;

            switch (msg.message)
            {
                case (int)WindowMessages.WM_KEYDOWN:
                case (int)WindowMessages.WM_LBUTTONDOWN:
                case (int)WindowMessages.WM_MBUTTONDOWN:
                case (int)WindowMessages.WM_RBUTTONDOWN:
                    //if (IsUserMode())
                    {
                        Focus();
                    }
                    break;
            }

            uint handled;
            IntPtr zeroPtr = IntPtr.Zero;
            int hr = _container.ProcessInput((uint)msg.message, (uint)msg.wParam, (uint)msg.lParam, ref zeroPtr, 0, 0, out handled);

            if (hr < 0 || handled == 0)
            {
                processed = false;
            }

            return processed;
        }

        protected virtual IntPtr WndProc(IntPtr hwnd, int message, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if (IsRenderingEnabledCore)
            {
                var msg = new System.Windows.Interop.MSG()
                {
                    hwnd = hwnd,
                    message = message,
                    wParam = wParam,
                    lParam = lParam
                };

                switch ((uint)message)
                {
                    case (int)WindowMessages.WM_SETCURSOR:
                    case (int)WindowMessages.WM_CONTEXTMENU:
                        if (IsMouseOver)
                        {
                            handled = true;
                            if (!ProcessInput(msg))
                            {
                                handled = DispatchMessage(msg);
                            }
                        }
                        break;

                    case (int)WindowMessages.WM_CHAR:
                    case (int)WindowMessages.WM_DEADCHAR:
                    case (int)WindowMessages.WM_SYSCHAR:
                    case (int)WindowMessages.WM_SYSDEADCHAR:
                    case (int)WindowMessages.WM_COMMAND:

                        if (IsFocused)
                        {
                            handled = true;
                            if (!ProcessInput(msg))
                            {
                                handled = DispatchMessage(msg);
                            }
                        }
                        break;

                    case (int)WindowMessages.WM_HELP:
                        if (IsFocused)
                        {
                            handled = true;
                            if (!ProcessInput(msg))
                            {
                                handled = DispatchMessage(msg);
                            }
                        }
                        break;

                    default:
                        bool dispatched = DispatchMessage(msg);
                        break;

                }
            }

            return IntPtr.Zero;
        }

        protected static void OnIsRenderingEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SilverlightWindowlessHost host = (SilverlightWindowlessHost)d;
            bool value = (bool)e.NewValue;

            if (host.IsLoaded && host.IsRenderingEnabledCore != value)
            {
                host.IsRenderingEnabledCore = value;
            }
        }

        protected static void OnBaseUrlChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as SilverlightWindowlessHost)._container.BaseUrl = (Uri)e.NewValue;
        }

        protected static void OnHostOptionsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as SilverlightWindowlessHost)._container.HostOptions = (XcpHostOptions)e.NewValue;
        }

        protected static void OnVersionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as SilverlightWindowlessHost)._container.Version = (Version)e.NewValue;
        }

        protected static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as SilverlightWindowlessHost)._container.Source = (Uri)e.NewValue;

            // Update Xaml dependency property
            var xaml = (d as SilverlightWindowlessHost).Xaml;
        }

        protected static void OnXamlChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as SilverlightWindowlessHost)._container.Xaml = (string)e.NewValue;

            // Update Source dependency property
            var source = (d as SilverlightWindowlessHost).Source;
        }

        protected static void OnInitParamsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as SilverlightWindowlessHost)._container.InitParams = (string)e.NewValue;
        }

        protected static void OnSplashScreenSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as SilverlightWindowlessHost)._container.SplashScreenSource = (Uri)e.NewValue;
        }

        #endregion

        #region Private Methods

        [DllImport("user32.dll")]
        private static extern bool TranslateMessage([In] ref System.Windows.Interop.MSG lpMsg);
        
        private void ReleaseResources()
        {
            if (_container != null)
            {
                IsRenderingEnabledCore = false;
                _container.Dispose();
                _container = null;
            }

            if (_renderer != null)
            {
                _renderer.Dispose();
                _renderer = null;
            }

            if (_window != null)
            {
                _window.Closed -= OnParentWindowClosed;
                _window = null;
            }

            if (_source != null)
            {
                _source.RemoveHook(WndProc);
                _source = null;
            }
        }

        private void OnSilverlightFrameRendered(object sender, FrameRenderedEventArgs e)
        {
            _renderer.RenderFrame(e);
        }

        private T GetProperty<T>(DependencyPropertyKey dpk, T value)
        {
            if (!object.Equals(value, GetValue(dpk.DependencyProperty)))
            {
                SetValue(dpk, value);
            }
            return value;
        }

        private T GetProperty<T>(DependencyProperty dp, T value)
        {
            if (!object.Equals(value, GetValue(dp)))
            {
                SetValue(dp, value);
            }
            return value;
        }

        [DllImport("user32.dll")]
        private static extern IntPtr GetDC(IntPtr hwnd);

        [DllImport("gdi32.dll")]
        public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);

        private static Vector GetDpiVector()
        {
            if (_dpiVector == null)
            {
                IntPtr desktop = GetDC(IntPtr.Zero);
                int pixelsPerInchX = GetDeviceCaps(desktop, 88); // LOGPIXELSX
                int pixelsPerInchY = GetDeviceCaps(desktop, 90); // LOGPIXELSY

                _dpiVector = new Vector((double)pixelsPerInchX / 96d, (double)pixelsPerInchY / 96d);
            }
            return _dpiVector.Value;
        }

        private static IntPtr MakeLParam(int wLow, int wHigh)
        {
            return (IntPtr)(((short)wHigh << 16) | (wLow & 0xffff));
        }

        private static int GetXLParam(IntPtr lParam)
        {
            return ((int)lParam & 0xffff);
        }

        private static int GetYLParam(IntPtr lParam)
        {
            return ((int)lParam >> 16);
        }

        #endregion

        #region ISilverlightHostEventProvider

        void ISilverlightHostEventProvider.OnCreated(EventArgs e)
        {
            // Getting these properties will update the corresponding dependency properties
            var created = IsCreated;
            var content = Content;
            var settings = Settings;

            OnCreated(e);
        }
        
        void ISilverlightHostEventProvider.OnSilverlightLoaded(EventArgs e)
        {
            // Update IsSilverlightLoaded dependency property
            var loaded = IsSilverlightLoaded;
            
            OnSilverlightLoaded(e);
        }

        void ISilverlightHostEventProvider.OnSilverlightError(SilverlightErrorEventArgs e)
        {
            // Update IsSilverlightLoaded dependency property
            var loaded = IsSilverlightLoaded;
            
            OnSilverlightError(e);
        }

        void ISilverlightHostEventProvider.OnSilverlightEvent(SilverlightEventArgs e)
        {
            OnSilverlightEvent(e);
        }

        void ISilverlightHostEventProvider.OnSourceDownloadComplete(EventArgs e)
        {
            OnSourceDownloadComplete(e);
        }

        void ISilverlightHostEventProvider.OnSourceDownloadProgressChanged(SourceDownloadProgressChangedEventArgs e)
        {
            OnSourceDownloadProgressChanged(e);
        }

        void ISilverlightHostEventProvider.OnContentLoaded(EventArgs e)
        {
            // Update IsSilverlightLoaded dependency property
            var loaded = IsSilverlightLoaded;
            
            OnContentLoaded(e);
        }

        void ISilverlightHostEventProvider.OnContentError(ErrorEventArgs e)
        {
            // Update IsSilverlightLoaded dependency property
            var loaded = IsSilverlightLoaded;
            
            OnContentError(e);
        }

        void ISilverlightHostEventProvider.OnContentZoomed(EventArgs e)
        {
            OnContentZoomed(e);
        }

        void ISilverlightHostEventProvider.OnContentResized(EventArgs e)
        {
            OnContentResized(e);
        }

        void ISilverlightHostEventProvider.OnFullScreenChanged(EventArgs e)
        {
            OnFullScreenChanged(e);
        }

        DownloadResult ISilverlightHostEventProvider.DownloadUrl(string url, IDownloadUrlCallback callback, out Stream stream)
        {
            return DownloadUrl(url, callback, out stream);
        }

        #endregion

        #region IDisposable

        ~SilverlightWindowlessHost()
        {
            Dispose(false);
        }

        /// <summary>
        /// Release resources held by the Silverlight plug-in host.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                ReleaseResources();

                //this.Loaded -= OnIsLoadedChanged;
                //this.Unloaded -= OnIsLoadedChanged;
            }
            
            _disposed = true;
        }

        #endregion
    }
}
